# 导入蓝图对象
# 同级目录下
from . import passport_blue
# 导入captcha工具，生成图片验证码
from info.utils.captcha.captcha import captcha
# 导入flask内置的对象
from flask import request, jsonify, current_app, make_response, session
# 导入自定义状态码
from info.utils.response_code import RET
# 导入redis实例
from info import redis_store, constants, db
# 导入正则
import re, random
# 导入云通讯
from info.libs.yuntongxun.sms import CCP
# 导入模型类
from info.models import User
# 导入日期模块
from datetime import datetime

# 注册
@passport_blue.route('/image_code')
def generate_image_code():
    """
    生成图片验证码：
    接受参数－－检查参数－－业务处理－－返回结果
    参数：前端传入uuid
    １、获取前端传入的uuid,查询字符串args
    ２、如果没有uuid，返回信息
    ３、调用captcha工具，生成图片验证码
    name名称,text文本,image图片
    4、把图片验证码的内容text，根据uuid来存入redis数据库中
    ５、返回图片
    :return:
    """
    # 获取参数
    image_code_id = request.args.get('image_code_id')
    # 检查参数是否存在,导入response_code.py文件
    if not image_code_id:
        #              错误编号　　　　　　　　错误信息
        return jsonify(errno=RET.PARAMERR, errmsg='参数错误')
    # 调用工具captcha生成图片验证码
    name, text, image = captcha.generate_captcha()
    # 把text存入redis数据库中，在info/__init__文件中实例化redis
    try:
        redis_store.setex('ImageCode_' + image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
    except Exception as e:
        # 把错误信息记录到项目日志中
        current_app.logger.error(e)
        # 直接终止程序运行
        return jsonify(errno=RET.DBERR, errmsg='存储数据失败')
    else:
        # 使用响应对象返回图片
        response = make_response(image)
        # 修改响应类型
        response.headers['Content-Type'] = 'image/jpg'
        # 返回响应,图片的路径
        return response


@passport_blue.route('/sms_code', methods=['POST'])
def send_sms_code():
    """
    发送短信
    接受参数－－检查参数－－业务处理－－返回结果
    １、获取参数，mobile,image_code,image_code_id
    ２、检查参数，参数必须全部存在
    ３、检查手机号的格式，使用正则
    ４、检查图片验证码是否正确
    ５、从redis中获取真实的图片验证码
    ６、判断获取结果是否存在
    ７、如果存在，先删除redis数据库中的图片验证码，
    因为图片验证码只能比较一次，比较一次的本质是只能读取redis数据库一次
    ８、比较图片验证码是否正确
    ９、生成短信验证码，６位数
    １０、把短信验证码存储在redis数据库中
    １１、调用云通讯发送短信
    １２、保存发送结果，用来判断发送是否成功
    １３、返回结果
    :return:
    """
    # 获取post请求的三个参数
    mobile = request.json.get('mobile')
    image_code = request.json.get('image_code')
    image_code_id = request.json.get('image_code_id')
    # 检查参数的完整性
    # if not mobile or not image_code or not image_code_id:
    if not all([mobile, image_code, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 检查手机号格式
    if not re.match(r'1[3456789]\d{9}$', mobile):
        return jsonify(error=RET.PARAMERR, errmsg='手机号格式错误')
    # 根据uuid从redis中获取图片验证码
    try:
        real_image_code = redis_store.get('ImageCode_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询数据失败')
    # 判断获取结果
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg='图片验证码已过期')
    # 如果获取到了图片验证码，需要把redis中的图片验证码删除，因为只能get一次
    try:
        redis_store.delete('ImageCode_' + image_code_id)
    except Exception as e:
        current_app.logger.error(e)
    # 比较图片验证码是否正确,忽略大小写;
    if real_image_code.lower() != image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg='图片验证码错误')
    # 根据手机号来查询用户未注册，使用模型类User
    try:
        user = User.query.filter(User.mobile == mobile).first()
        # User.query.filter_by(mobile=mobile).first()
        # 不能使用get
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询用户数据失败')
    else:
        # 判断查询结果
        # if user:
        if user is not None:
            return jsonify(errno=RET.DATAEXIST, errmsg='手机号已注册')
    # 业务处理
    # 生成短信随机码
    sms_code = '%06d' % random.randint(0, 999999)
    print(sms_code)
    # 存入redis中
    try:
        redis_store.setex('SMSCode_' + mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='保存数据失败')
    # 调用云通讯发送短信
    try:
        # 模块中CCP是个实例函数
        # 构造发送短信对象
        ccp = CCP()
        # 第一个参数是手机号，第二个参数是短信内容，第三个参数是模板id
        result = ccp.send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES/60], 1)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.THIRDERR, errmsg='发送短信异常')
    # 判断发送结果
    if result == 0:
        return jsonify(errno=RET.OK, errmsg='发送成功')
    else:
        return jsonify(errno=RET.THIRDERR, errmsg='发送失败')


@passport_blue.route('/register', methods=['POST'])
def register():
    """
    用户注册：本质是把用户信息存入mysql数据库中。
    接受参数－－检查参数－－业务处理－－返回结果
    １、获取参数，mobile,sms_code,password
    ２、检查参数的完整性(接受的参数不一定完整)
    ３、检查手机号的格式(更严谨)
    ４、检查短信验证码是否正确
    ５、尝试从redis数据库中获取真实的短信验证码，根据mobile
    ６、判断获取结果，如果不存在，表示短信验证码已过期
    ７、比较短信验证码是否正确
    ８、删除已经比较过的redis中的短信验证码
    短信验证码是先比较，再删除的过程，因为短信验证码可以比较多次
    ９、需要使用模型类对象，对密码进行加密存储
    １０、确认用户未注册，根据手机号查询用户
    １１、保存用户信息，提交数据，若发生异常，需要回滚(在这之前需判断用户是否已经注册)
    １２、缓存用户信息，用户id
    １３、返回结果
    :return:
    """
    # 获取参数　POST请求
    mobile = request.json.get('mobile')
    sms_code = request.json.get('sms_code')
    password = request.json.get('password')
    # 检查参数的完整性
    if not all([mobile, sms_code, password]):
        # 参数不完整
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 检查手机号的格式
    if not re.match(r'1[3456789]\d{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式错误')
    # 检查短信验证码是否正确
    # 一个是redis数据库中存储的真实验证码，一个是用户在页面输入的验证码
    real_sms_code = None
    try:
        # 真实的验证码
        real_sms_code = redis_store.get('SMSCode_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
        # 操作数据库，用DBERR
        jsonify(errno=RET.DBERR, errmsg='查询数据失败')
    # 判断查询结果是否存在
    if not real_sms_code:
        # 此时查询结果不存在，应该是NODATA
        return jsonify(errno=RET.NODATA, errmsg='短信验证码已过期')
    # 比较短信验证码是否正确,为了确保客户端验证码为string
    if real_sms_code != str(sms_code):
        return jsonify(errno=RET.DATAERR, errmsg='短信验证码错误')
    # 删除redis中存储的短信验证码
    try:
        redis_store.delete('SMSCode_' + mobile)
    except Exception as e:
        current_app.logger.error(e)
    # 根据手机号查询用户是否注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
        # user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        # 涉及到后台数据库查询操作，DBERR
        return jsonify(errno=RET.DBERR, errmsg='查询用户信息失败')
    else:
        if user:
            return jsonify(errno=RET.DATAERR, errmsg='手机号已注册')
    # 构造模型类对象，存储用户信息，实现密码加密存储
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # 实际上调用了模型类中的密码加密方法，generate_password_hash方法，werkzeug提供
    user.password = password
    # print(user)
    # 提交用户数据到mysql数据库中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 如果发生异常，需要进行回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存用户信息失败')
    # 缓存用户信息，实现状态保持，使用请求上下文对象session
    session['user_id'] = user.id
    session['mobile'] = mobile
    session['nick_name'] = mobile
    # 返回结果
    return jsonify(errno=RET.OK, errmsg='OK')


@passport_blue.route('/login', methods=['POST'])
def login():
    """
    用户登录
    接受参数－－检查参数－－业务处理－－返回结果
    １、获取参数，mobile,password
    ２、检查参数的完整性
    ３、检查手机号的格式
    ４、根据手机号查询用户信息，确认用户已注册
    ５、确认查询结果
    ６、确认密码正确
    ７、保存用户的登录时间
    ８、提交数据到mysql数据库中
    ９、缓存用户信息
    因为用户可以登录多次，有可能会修改用户昵称，若修改用户昵称，是修改后的结果；如果没有修改，默认是手机号　
    session['nick_name'] = user.nick_name
    １０、返回结果
    :return:
    """
    # 获取参数
    mobile = request.json.get('mobile')
    password = request.json.get('password')
    # 检查参数的完整性
    if not all([mobile, password]):
        # 检查参数完整性，用PARAMERR
        return jsonify(errno=RET.PARAMERR, errmsg='参数缺失')
    # 检查手机号的格式
    if not re.match(r'1[3456789]\d{9}$', mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号格式错误')
    # 根据手机号查询用户信息，确认用户是否已注册
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(error=RET.DBERR, errmsg='查询用户数据失败')
    # 确认查询结果
    # if user is None:
    #     return jsonify(errno=RET.DBERR, errmsg='用户未注册')
    # # 确认密码正确
    # if not user.check_password(password):
    #     return jsonify(errno=RET.PWDERR, errmsg='密码错误')
    # 用户已注册和密码正确在一起判断
    if not user.check_password(password) and user is None:
        return jsonify(errno=RET.DATAERR, errmsg='用户名或密码错误')
    # 保存用户的登录时间
    user.last_login = datetime.now()
    # 提交数据到mysql数据库中
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg='保存用户数据失败')
    # 缓存用户信息
    session['user_id'] = user.id
    session['mobile'] = mobile
    # 注意：缓存用户信息和注册时的缓存不一样，因为用户可以登录多次，有可能会修改用户昵称
    # 确保redis中的缓存数据与mysql数据库中的真实数据保持一致
    session['nick_name'] = user.nick_name
    # 返回结果
    return jsonify(errno=RET.OK, errmsg='OK')


@passport_blue.route('/logout')
def logout():
    """
    用户退出
    本质：清除redis中缓存的用户信息

    :return:
    """
    session.pop('user_id', None)
    session.pop('mobile', None)
    session.pop('nick_name', None)
    # 返回结果
    return jsonify(errno=RET.OK, errmsg='OK')









    pass
